/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package pdasim;

import com.mxgraph.swing.mxGraphComponent;
import com.mxgraph.util.mxConstants;
import com.mxgraph.view.mxGraph;
import com.mxgraph.view.mxStylesheet;
import java.util.ArrayList;
import java.io.File;
import java.io.IOException;
import java.util.Hashtable;
import java.util.List;
import java.util.Stack;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;

/**
 *
 * @author Camilo
 */
public class PDA {

    private String dir;
    private ArrayList<NODO> listanodo;
    private ArrayList<Vertex> vertexs;
    private Stack<String> stack;
    private Stack<String> log;
    private boolean status_;
    private int cantpush;

    public PDA(String dir) {
        this.dir = dir;
        stack = new Stack<String>();
        log = new Stack<String>();
        stack.push("Z");
        status_ = true;

    }

    public void generar() {
        listanodo = new ArrayList<NODO>();
        SAXBuilder builder = new SAXBuilder();
        File xmlFile = new File(dir);
        try {

            Document document = (Document) builder.build(xmlFile);
            Element rootNode = document.getRootElement();
            Element type = rootNode.getChild("type");
            if (type.getText().equals("pda")) {
                rootNode = rootNode.getChild("automaton");
                List liststate = rootNode.getChildren("state");
                List listtransition = rootNode.getChildren("transition");

                for (int i = 0; i < liststate.size(); i++) {

                    Element nodexml = (Element) liststate.get(i);
                    int id = Integer.parseInt(nodexml.getAttributeValue("id"));
                    String name = nodexml.getAttributeValue("name");
                    double xi = Double.parseDouble(nodexml.getChildText("x"));
                    double yi = Double.parseDouble(nodexml.getChildText("y"));
                    int x = (int) xi;
                    int y = (int) yi;
                    boolean inicio = false;
                    boolean finale = false;
                    boolean status = false;
                    if (nodexml.getChild("final") != null) {
                        finale = true;
                    }
                    if (nodexml.getChild("initial") != null) {
                        inicio = true;
                        status = true;
                    }
                    NODO node = new NODO(id, name, x, y, inicio, finale, status);
                    listanodo.add(node);

                }
                for (int i = 0; i < listtransition.size(); i++) {
                    Element tranxml = (Element) listtransition.get(i);
                    int id = Integer.parseInt(tranxml.getChildText("from"));
                    int to = Integer.parseInt(tranxml.getChildText("to"));

                    String read = tranxml.getChildText("read");
                    if (read.equals("")) {
                        read = "Ɛ";
                    }
                    String pod = tranxml.getChildText("pop");
                    if (pod.equals("")) {
                        pod = "Ɛ";
                    }
                    String push = tranxml.getChildText("push");
                    if (push.equals("")) {
                        push = "Ɛ";
                    }
                    boolean actual = false;
                    Transition transition = new Transition(to, read, pod, push, actual);
                    int idnodo = -1;
                    for (int j = 0; j < listanodo.size(); j++) {
                        if (id == listanodo.get(j).getId()) {
                            idnodo = j;
                        }

                    }
                    listanodo.get(idnodo).addtransition(transition);

                }
            } else {
                System.out.println("No es un pda");
            }



        } catch (IOException io) {
            System.out.println(io.getMessage());
        } catch (JDOMException jdomex) {
            System.out.println(jdomex.getMessage());
        }



    }

    public String pdastep(String lineinput) {
        setCantpush(0);
        String line = lineinput;
        int posactual = -1;
        for (int i = 0; i < listanodo.size(); i++) {
            if (listanodo.get(i).isStatus()) {
                posactual = i;
                break;
            }
        }
        char charin = line.charAt(0);
        String in = String.valueOf(charin);
        log.push("--------------------");

        int transitionactual = -1;
        int posdelep = -1;
        for (int i = 0; i < listanodo.get(posactual).getTransitions().size(); i++) {
            if (listanodo.get(posactual).getTransitions().get(i).getRead().equals(in)) {
                transitionactual = i;
            }
            if (listanodo.get(posactual).getTransitions().get(i).getRead().equals("Ɛ")) {
                posdelep = i;
            }
        }

        if (transitionactual > -1) {
            log.push("Transicion con: " + in);
            String peek = stack.peek();
            if (peek.equals(listanodo.get(posactual).getTransitions().get(transitionactual).getPop())) {
                log.push("Pop: " + stack.peek());
                stack.pop();
                String push = listanodo.get(posactual).getTransitions().get(transitionactual).getPush();
                if (!push.equals("Ɛ")) {
                    push = new StringBuffer(push).reverse().toString();

                    for (int i = 0; i < push.length(); i++) {
                        stack.push(String.valueOf(push.charAt(i)));
                        log.push("Push: " + stack.peek());
                        setCantpush(getCantpush() + 1);
                    }
                }
                listanodo.get(posactual).setStatus(false);
                int nextstate = listanodo.get(posactual).getTransitions().get(transitionactual).getTo();
                int posnext = findnodo(nextstate);
                listanodo.get(posnext).setStatus(true);
                line = line.substring(1);
                if (line.equals("")) {
                    line = "~";
                }


            } else {
                log.push("ERROR");
                log.push("SE ESPERABA OTRO CARACTER ");
                log.push("EN EL TOPE DE LA PILA");
                this.status_ = false;
            }
        } else {
            if (posdelep > -1) {
                log.push("Transicion con: Ɛ");
                String peek = stack.peek();
                if (peek.equals(listanodo.get(posactual).getTransitions().get(posdelep).getPop())) {
                    log.push("Pop: " + stack.peek());
                    stack.pop();
                    String push = listanodo.get(posactual).getTransitions().get(posdelep).getPush();
                    if (!push.equals("Ɛ")) {
                        push = new StringBuffer(push).reverse().toString();

                        for (int i = 0; i < push.length(); i++) {
                            stack.push(String.valueOf(push.charAt(i)));
                            log.push("Push: " + stack.peek());
                            setCantpush(getCantpush() + 1);
                        }
                    }
                    listanodo.get(posactual).setStatus(false);
                    int nextstate = listanodo.get(posactual).getTransitions().get(posdelep).getTo();
                    int posnext = findnodo(nextstate);
                    listanodo.get(posnext).setStatus(true);
                    if (line.equals("~")) {
                        log.push("FIN DE LA CADENA");

                    }
                } else {
                    log.push("ERROR");
                    log.push("SE ESPERABA OTRO CARACTER ");
                    log.push("EN EL TOPE DE LA PILA");
                    this.status_ = false;
                }
            } else {
                if (line.equals("~") && listanodo.get(posactual).isFinale()) {
                    log.push("ESTADO ACEPTADO");
                    this.status_ = false;
                } else {
                    log.push("ERROR");
                    log.push("No hay Transicion");
                    this.status_ = false;
                }
            }
        }

        return line;
    }

    public mxGraphComponent paint() {
        mxGraphComponent graphComponent = null;
        mxGraph graph = new mxGraph();
        Object parent = graph.getDefaultParent();
        mxStylesheet stylesheet = graph.getStylesheet();
        Hashtable<String, Object> style = new Hashtable<String, Object>();
        style.put(mxConstants.STYLE_SHAPE, mxConstants.SHAPE_ELLIPSE);
        stylesheet.putCellStyle("NORMAL", style);
        style = new Hashtable<String, Object>();
        style.put(mxConstants.STYLE_SHAPE, mxConstants.SHAPE_DOUBLE_ELLIPSE);
        stylesheet.putCellStyle("FINAL", style);
        style = new Hashtable<String, Object>();
        style.put(mxConstants.STYLE_SHAPE, mxConstants.SHAPE_TRIANGLE);
        stylesheet.putCellStyle("START", style);
        style = new Hashtable<String, Object>();
        style.put(mxConstants.STYLE_EDGE, mxConstants.EDGESTYLE_LOOP);
        style.put(mxConstants.STYLE_DIRECTION, mxConstants.DIRECTION_NORTH);
        style.put(mxConstants.STYLE_ROUNDED, true);
        stylesheet.putCellStyle("ARROWLOOP", style);
        style = new Hashtable<String, Object>();
        style.put(mxConstants.STYLE_EDGE, mxConstants.EDGESTYLE_LOOP);
        style.put(mxConstants.STYLE_ROUNDED, true);
        style.put(mxConstants.STYLE_LOOP, parent);
        style.put(mxConstants.STYLE_DIRECTION, mxConstants.DIRECTION_EAST);
        stylesheet.putCellStyle("ARROWE", style);
        style = new Hashtable<String, Object>();
        style.put(mxConstants.STYLE_EDGE, mxConstants.EDGESTYLE_LOOP);
        style.put(mxConstants.STYLE_ROUNDED, true);
        style.put(mxConstants.STYLE_LOOP, parent);
        style.put(mxConstants.STYLE_DIRECTION, mxConstants.DIRECTION_EAST);
        stylesheet.putCellStyle("ARROWW", style);

        graph.getModel().beginUpdate();
        try {
            vertexs = new ArrayList<Vertex>();
            for (int i = 0; i < listanodo.size(); i++) {
                Object vertex_in = null;
                if (listanodo.get(i).isFinale()) {
                    if (listanodo.get(i).isStatus()) {
                        vertex_in = graph.insertVertex(parent, null, listanodo.get(i).getName(), listanodo.get(i).getX(), listanodo.get(i).getY(), 40, 40, "FINAL;fillColor=green");
                    } else {
                        vertex_in = graph.insertVertex(parent, null, listanodo.get(i).getName(), listanodo.get(i).getX(), listanodo.get(i).getY(), 40, 40, "FINAL");
                    }
                } else {
                    if (listanodo.get(i).isInicial()) {
                        graph.insertVertex(parent, null, "", listanodo.get(i).getX() - 30, listanodo.get(i).getY() + 10, 25, 25, "START");
                    }
                    if (listanodo.get(i).isStatus()) {
                        vertex_in = graph.insertVertex(parent, null, listanodo.get(i).getName(), listanodo.get(i).getX(), listanodo.get(i).getY(), 40, 40, "NORMAL;fillColor=green");
                    } else {
                        vertex_in = graph.insertVertex(parent, null, listanodo.get(i).getName(), listanodo.get(i).getX(), listanodo.get(i).getY(), 40, 40, "NORMAL");
                    }
                }

                Vertex vertex = new Vertex(vertex_in, listanodo.get(i).getId());
                vertexs.add(vertex);
            }

            for (int i = 0; i < vertexs.size(); i++) {
                NODO from = findNodobyid(vertexs.get(i).getId());
                if (from.getTransitions().size() > 0) {
                    for (int j = 0; j < from.getTransitions().size(); j++) {
                       
                        Vertex to = findvertex(from.getTransitions().get(j).getTo());
                        NODO to2 = findNodobyid(from.getTransitions().get(j).getTo());
                        if (from.getId() == to2.getId()) {

                            String label = from.getTransitions().get(j).getRead() + "," + from.getTransitions().get(j).getPop() + "/" + from.getTransitions().get(j).getPush();
                            graph.insertEdge(parent, null, label, vertexs.get(i).getVertex(), to.getVertex(), "ARROWLOOP");
                        } else {
                            if (findtransition(from.getId(), to2.getTransitions())) {
                                if (to2.getY() > from.getY()) {
                                    String label = from.getTransitions().get(j).getRead() + "," + from.getTransitions().get(j).getPop() + "/" + from.getTransitions().get(j).getPush();
                                    graph.insertEdge(parent, null, label, vertexs.get(i).getVertex(), to.getVertex(), "ARROWE");
                                } else {
                                    String label = from.getTransitions().get(j).getRead() + "," + from.getTransitions().get(j).getPop() + "/" + from.getTransitions().get(j).getPush();
                                    graph.insertEdge(parent, null, label, vertexs.get(i).getVertex(), to.getVertex(), "ARROWW");
                                }
                            } else {
                                String label = from.getTransitions().get(j).getRead() + "," + from.getTransitions().get(j).getPop() + "/" + from.getTransitions().get(j).getPush();
                                graph.insertEdge(parent, null, label, vertexs.get(i).getVertex(), to.getVertex());
                            }
                        }
                    }

                }
            }







        } finally {
            graph.getModel().endUpdate();
        }

        graphComponent = new mxGraphComponent(graph);
        return graphComponent;

    }

    public boolean findtransition(int id, ArrayList<Transition> transitions) {
        boolean findit = false;
        for (int i = 0; i < transitions.size(); i++) {
            if (id == transitions.get(i).getTo()) {
                findit = true;
            }
        }
        return findit;
    }

    public int findnodo(int id) {
        int pos = -1;

        for (int i = 0; i < listanodo.size(); i++) {
            if (id == listanodo.get(i).getId()) {
                pos = i;
                break;
            }
        }

        return pos;
    }

    public Vertex findvertex(int id) {
        Vertex ver = null;
        for (int i = 0; i < this.vertexs.size(); i++) {
            if (vertexs.get(i).getId() == id) {
                ver = vertexs.get(i);
            }
        }
        return ver;
    }

    /**
     * @return the dir
     */
    public String getDir() {
        return dir;
    }

    /**
     * @param dir the dir to set
     */
    public void setDir(String dir) {
        this.dir = dir;
    }

    /**
     * @return the listanodo
     */
    public ArrayList<NODO> getListanodo() {
        return listanodo;
    }

    /**
     * @param listanodo the listanodo to set
     */
    public void setListanodo(ArrayList<NODO> listanodo) {
        this.listanodo = listanodo;
    }

    public NODO findNodobyid(int id) {
        NODO nodo = null;
        for (int i = 0; i < listanodo.size(); i++) {
            if (id == listanodo.get(i).getId()) {
                nodo = listanodo.get(i);
            }
        }
        return nodo;
    }

    /**
     * @return the stack
     */
    public Stack<String> getStack() {

        return stack;
    }

    /**
     * @param stack the stack to set
     */
    public void setStack(Stack<String> stack) {
        this.stack = stack;
    }

    /**
     * @return the log
     */
    public Stack<String> getLog() {
        return log;
    }

    /**
     * @param log the log to set
     */
    public void setLog(Stack<String> log) {
        this.log = log;
    }

    /**
     * @return the status_
     */
    public boolean isStatus_() {
        return status_;
    }

    /**
     * @param status_ the status_ to set
     */
    public void setStatus_(boolean status_) {
        this.status_ = status_;
    }

    /**
     * @return the cantpush
     */
    public int getCantpush() {
        return cantpush;
    }

    /**
     * @param cantpush the cantpush to set
     */
    public void setCantpush(int cantpush) {
        this.cantpush = cantpush;
    }
}
